package com.gframework.mybatis.util.generator.core.conf;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import com.gframework.mybatis.util.generator.core.codegen.NeedCanUpdate;
import com.gframework.mybatis.util.generator.core.codegen.SqlOperatorGenerator;
import com.gframework.mybatis.util.generator.core.interceptor.JavaBeanInterceptor;
import com.gframework.mybatis.util.generator.core.interceptor.JavaDaoInterceptor;
import com.gframework.mybatis.util.generator.core.interceptor.XmlElementInterceptor;

/**
 * 配置信息封装类.
 * 
 * @since 1.0.0
 * @author Ghwolf
 */
public class Configuration {

	/** 数据库连接url */
	private String dburl;
	/** 数据库连接参数 */
	private Properties jdbcParam = new Properties();
	/** true：覆盖现在的实体类，false：不做任何事情。默认时true */
	private boolean writerableBean = true;
	/** true：覆盖现在的DAO，false：不做任何事情。默认时false */
	private boolean writerableDao = false;
	/** true：覆盖现在的XML，false：不做任何事情。默认时true */
	private boolean writerableXml = true;
	/** 代码基础包（bean所在位置=*.entity.pojo，dao所在位置=*.dao，xml所在位置=*.dao） */
	private String basePackage = "com.gframework.biz";
	/** 存放源代码的src目录所在位置，如果不设置则会尝试自己去寻找 */
	private String baseSrc;
	/** true表示在生成@Table注解等相关注解的时候，生成catalog,schema等内容，false则不会生成这两个内容 */
	private boolean createCatalogAndSchema = true;
	/** javaBean创建拦截器 */
	private JavaBeanInterceptor javaBeanInterceptor;
	/** javaDao创建拦截器 */
	private JavaDaoInterceptor javaDaoInterceptor;
	/** xmlElement创建拦截器 */
	private XmlElementInterceptor xmlElementInterceptor;
	/** 自定义sql生成器class对象集合 */
	private List<Class<? extends SqlOperatorGenerator>> customSqlGenerators = new ArrayList<>();
	/** 自定义sql生成器所在包 */
	private List<String> customSqlGeneratorPackage = new ArrayList<>();
	/** 忽略的表名称前缀(不区分大小写)（在生成实体类名称等代码的时候，不会将忽略的内容给包含进去） */
	private List<String> ignoreTableNamePrefix = new ArrayList<>(8);
	/** 忽略的表名称后缀(不区分大小写)（在生成实体类名称等代码的时候，不会将忽略的内容给包含进去） */
	private List<String> ignoreTableNameSuffix = new ArrayList<>(8);
	/** 是否仅使用常用类型，如果为true，则不会出现byte、short、float、类型，而是采用Integer和Double代替。 */
	private boolean onlyCommonType = true ;
	/** 是否不生成xml，如果你使用的是Provider方式处理sql，那么就不需要生成xml */
	private boolean noXml = false ;
	/**
	 * 是否生成列名称，表名称常量，如果生成，那么pojo类除了基本属性字段外，还会生成同等数量的常量，分别代表每一个字段的列名称以及一个表名称常量。
	 * <p>这样的好处就是，可以将列名称和属性名称解耦和，开发彻底不用再关注列的真名是什么，是需要应用常量即可，对于列名可能出现的变化，也可以轻松处理。
	 */
	private boolean createNameConst = true ;
	/**
	 * 如果为true，则表示{@link NeedCanUpdate}接口将无效，不会进行此接口的相关判断.
	 * <p>如果你希望视图等表也生成增删改相关sql，那么可以将其设置为true
	 */
	private boolean needCanUpdateInterfaceInvalid = false;
	/**
	 * 对表按照名称进行归类.
	 * <li>key：用于匹配的表</li>
	 * <li>value：模块名称（生成是也会当作包名称来处理）</li>
	 */
	private Map<DbFullName, String> tableClassifies = new HashMap<>();
	/**
	 * 数据表中忽略的列.
	 * <li>key：用于匹配的表</li>
	 * <li>value：忽略的列名称集合</li>
	 */
	private Map<DbFullName, List<String>> ignoreColumns = new HashMap<>();

	/**
	 * 添加一个jdbc连接参数
	 * 
	 * @param key 参数名称，不能为null
	 * @param value 参数值，如果为null则按照""处理
	 * @throws NullPointerException 如果key为null，则抛出此异常
	 */
	public void addJdbcUrlParam(String key, String value) {
		if (key == null) {
			throw new NullPointerException("设置urlParam，key不能为null！");
		}
		this.jdbcParam.put(key, value == null ? "" : value);
	}

	/**
	 * 添加多个jdbc连接参数
	 * 
	 * @param params 可以是map、properties，key表示参数名称（为空则跳过），value表示值（为null则按照""处理），
	 *            如果params本身为null，这不作任何事情
	 */
	public void addJdbcUrlParam(Map<String, String> params) {
		if (params == null) {
			return;
		}
		for (Map.Entry<String, String> entry : params.entrySet()) {
			String key = entry.getKey();
			if (key != null && !"".equals(key)) {
				this.addJdbcUrlParam(key, entry.getValue());
			}
		}
	}

	/**
	 * 设置要进行生成的表.
	 * <p>可以设置多个。
	 * 
	 * @param catalog 所属catalog(没有或表示任意可设为null)
	 * @param schema 所属schema(没有或表示任意可设为null)
	 * @param tableReg 表名称正则表达式（如果多个存在交集，则会重复生成）
	 * @param classify 所属模块名称，也是所属包名称，会自动转小写
	 * @throws NullPointerException 当参数有一个为null时
	 * @throws IllegalArgumentException 当classify不是一个合法的包名称时
	 */
	public void setTable(String catalog, String schema, String tableReg, String classify) {
		if (tableReg == null || classify == null) {
			throw new NullPointerException("tableReg和classify不能为null。");
		}
		if (!classify.matches("\\w+")) {
			throw new IllegalArgumentException("classify必须是一个合法的包名称。");
		}
		this.tableClassifies.put(new DbFullName(catalog, schema, tableReg), classify.toLowerCase());
	}

	/**
	 * 生成时忽略某个列，适用于所有表.
	 * 
	 * @param colName 列名称
	 */
	public void ignoreColumn(String colName) {
		this.ignoreColumn(colName, null, null, ".*");
	}

	/**
	 * 生成时忽略某个列，适用于特定的表.
	 * 
	 * @param colName 列名称
	 * @param catalog 所属catalog(没有或表示任意可设为null)
	 * @param schema 所属schema(没有或表示任意可设为null)
	 * @param tableReg 表名称正则表达式
	 */
	public void ignoreColumn(String colName, String catalog, String schema, String tableReg) {
		List<String> list = this.ignoreColumns.get(new DbFullName(catalog,schema,tableReg));
		if (list == null) {
			list = new ArrayList<>();
			this.ignoreColumns.put(new DbFullName(catalog, schema, tableReg), list);
		}
		list.add(colName);
	}

	/**
	 * 设置数据库连接信息.
	 * 
	 * @param dburl 数据库连接url
	 */
	public void setJdbc(String dburl) {
		this.setJdbc(dburl, null, null);
	}

	/**
	 * 设置数据库连接信息.
	 * 
	 * @param dburl 数据库连接url
	 * @param username 数据库用户
	 * @param password 数据库密码
	 */
	public void setJdbc(String dburl, String username, String password) {
		this.dburl = dburl;
		// 兼容sqlite
		if (username != null) {
			this.jdbcParam.put("user", username);
		}
		if (password != null) {
			this.jdbcParam.put("password", password);
		}
	}

	/**
	 * 设置是否覆盖现有的实体类，否则备份.
	 * 默认true
	 * 
	 * @param writerableBean true覆盖，false不做任何事情
	 */
	public void setWriterableBean(boolean writerableBean) {
		this.writerableBean = writerableBean;
	}

	/**
	 * 设置是否覆盖现有的DAO，否则备份.
	 * 默认false
	 * 
	 * @param writerableDao true覆盖，false不做任何事情
	 */
	public void setWriterableDao(boolean writerableDao) {
		this.writerableDao = writerableDao;
	}

	/**
	 * 设置代码所在基础包的位置.<br>
	 * POJO生成位置：*.entity.pojo
	 * DAO生成位置：*.dao
	 * XML生成位置：*.dao
	 * 
	 * @param basePackage 包位置
	 */
	public void setBasePackage(String basePackage) {
		this.basePackage = basePackage;
	}

	/**
	 * 设置源代码src目录所在位置，如果不设置则会尝试自动去寻找.<br>
	 * 如果你是按照标准的maven目录结构构建的项目，那么完全不用去设置，否则需要设置。
	 * 
	 * @param baseSrc src全路径
	 */
	public void setBaseSrc(String baseSrc) {
		this.baseSrc = baseSrc;
	}

	/**
	 * true表示在生成@Table注解等相关注解的时候，生成catalog,schema等内容，false则不会生成这两个内容.<br>
	 * 默认为true
	 * 
	 * @param createCatalogAndSchema true表示生成catalog,schema注解内容，false则表示不生成
	 */
	public void setCreateCatalogAndSchema(boolean createCatalogAndSchema) {
		this.createCatalogAndSchema = createCatalogAndSchema;
	}

	/**
	 * 设置是否覆盖现有的Xml，否则备份.
	 * 默认true
	 * 
	 * @param writerableXml true覆盖，false不做任何事情
	 */
	public void setWriterableXml(boolean writerableXml) {
		this.writerableXml = writerableXml;
	}

	/**
	 * 设置 创建javaBean 拦截器.
	 * 
	 * @param javaBeanInterceptor javaBean创建拦截器
	 */
	public void setJavaBeanInterceptor(JavaBeanInterceptor javaBeanInterceptor) {
		this.javaBeanInterceptor = javaBeanInterceptor;
	}

	/**
	 * 设置 创建javaDao 拦截器.
	 * 
	 * @param javaDaoInterceptor javaDao创建拦截器
	 */
	public void setJavaDaoInterceptor(JavaDaoInterceptor javaDaoInterceptor) {
		this.javaDaoInterceptor = javaDaoInterceptor;
	}

	/**
	 * 设置一个自定义sql生成器所在包
	 * 
	 * @param packageName 包全名
	 * @see SqlOperatorGenerator
	 */
	public void addSqlGeneratorPackage(String packageName) {
		this.customSqlGeneratorPackage.add(packageName);
	}

	/**
	 * 设置一个自定义sql生成器的class对象
	 * 
	 * @param sqlGeneratorClass SqlOperatorGenerator接口子类
	 * @see SqlOperatorGenerator
	 */
	public void addSqlGenerators(Class<? extends SqlOperatorGenerator> sqlGeneratorClass) {
		this.customSqlGenerators.add(sqlGeneratorClass);
	}

	/**
	 * 如果为true，则表示{@link NeedCanUpdate}接口将无效，不会进行此接口的相关判断.
	 * 这将意味着所有的表（包括视图和同义词等）都会生成增删改方法。
	 * <p>
	 * 如果你希望视图等表也生成增删改相关sql，那么可以将其设置为true。
	 * </p>
	 * 
	 * @param needCanUpdateInterfaceInvalid NeedCanUpdate接口是否失效，默认false
	 */
	public void setNeedCanUpdateInterfaceInvalid(boolean needCanUpdateInterfaceInvalid) {
		this.needCanUpdateInterfaceInvalid = needCanUpdateInterfaceInvalid;
	}

	/**
	 * 设置一个mybatis xml节点生成拦截器.
	 * 
	 * @param xmlElementInterceptor XmlElementInterceptor接口对象
	 */
	public void setXmlElementInterceptor(XmlElementInterceptor xmlElementInterceptor) {
		this.xmlElementInterceptor = xmlElementInterceptor;
	}

	/**
	 * 添加 忽略的表名称前缀(不区分大小写)（在生成实体类名称等代码的时候，不会将忽略的内容给包含进去）
	 * 
	 * @param prefix 名称前缀
	 */
	public void addIgnoreTableNamePrefix(String prefix) {
		this.ignoreTableNamePrefix.add(prefix);
	}

	/**
	 * 添加 忽略的表名称后缀(不区分大小写)（在生成实体类名称等代码的时候，不会将忽略的内容给包含进去）
	 * 
	 * @param suffix 名称后缀
	 */
	public void addIgnoreTableNameSuffix(String suffix) {
		this.ignoreTableNameSuffix.add(suffix);
	}

	/**
	 * 取得忽略的表名称后缀(不区分大小写)（在生成实体类名称等代码的时候，不会将忽略的内容给包含进去）
	 * 
	 * @return 名称后缀集合（不为null）
	 */
	public List<String> getIgnoreTableNameSuffix() {
		return this.ignoreTableNameSuffix;
	}

	/**
	 * 取得忽略的表名称前缀(不区分大小写)（在生成实体类名称等代码的时候，不会将忽略的内容给包含进去）
	 * 
	 * @return 名称前缀集合（不为null）
	 */
	public List<String> getIgnoreTableNamePrefix() {
		return this.ignoreTableNamePrefix;
	}

	/**
	 * 取得自定义sql生成器所在包
	 * 
	 * @return 自定义sql生成器所在包
	 */
	public List<String> getCustomSqlGeneratorPackage() {
		return this.customSqlGeneratorPackage;
	}

	/**
	 * 取得自定义sql生成器class对象集合
	 * 
	 * @return 自定义sql生成器class对象集合
	 */
	public List<Class<? extends SqlOperatorGenerator>> getCustomSqlGenerators() {
		return this.customSqlGenerators;
	}

	/**
	 * 取得javaDao创建拦截器
	 * 
	 * @return javaDao创建拦截器
	 */
	public JavaDaoInterceptor getJavaDaoInterceptor() {
		return this.javaDaoInterceptor;
	}

	/**
	 * 取得javaBean创建拦截器
	 * 
	 * @return javaBean创建拦截器
	 */
	public JavaBeanInterceptor getJavaBeanInterceptor() {
		return this.javaBeanInterceptor;
	}

	/**
	 * 取得忽略生成的列
	 * 
	 * @return 返回的是一个Map集合，其中key表示的是表，value是一个list集合，包含的有要忽略的列
	 */
	public Map<DbFullName, List<String>> getIgnoreColumns() {
		return this.ignoreColumns;
	}

	/**
	 * 取得要生成的表，key表示表名称，value表示模块名称
	 * 
	 * @return 要生成的表，key表示表名称，value表示模块名称
	 */
	public Map<DbFullName, String> getTableClassifies() {
		return this.tableClassifies;
	}

	/**
	 * 存放源代码的src目录所在位置，如果不设置则会尝试自己去寻找
	 * 
	 * @return 取得源代码路径
	 */
	public String getBaseSrc() {
		return this.baseSrc;
	}

	/**
	 * 取得代码基础包名称
	 * 
	 * @return 代码基础包名称
	 */
	public String getBasePackage() {
		return this.basePackage;
	}

	/**
	 * 是否复写bean的代码
	 * 
	 * @return true表示会重新生成bean，false表示如果bean已经存在，则不做任何事情。
	 */
	public boolean isWriterableBean() {
		return this.writerableBean;
	}

	/**
	 * 是否复写dao的代码
	 * 
	 * @return true表示会重新生成dao，false表示如果dao已经存在，则不做任何事情。
	 */
	public boolean isWriterableDao() {
		return this.writerableDao;
	}

	/**
	 * 是否复写xml的代码
	 * 
	 * @return true表示会重新生成xml，false表示如果xml已经存在，则不做任何事情。
	 */
	public boolean isWriterableXml() {
		return this.writerableXml;
	}

	/**
	 * 取得数据库连接url
	 * 
	 * @return 数据库连接url
	 */
	public String getDburl() {
		return this.dburl;
	}

	/**
	 * 取得数据库连接参数.
	 * <p>
	 * <strong>最好不要在外部随意修改此方法返回值中的内容，因为他不是一个副本，他会真实的影响到生成代码</strong>
	 * </p>
	 * 
	 * @return 返回Properties类对象
	 */
	public Properties getJdbcParam() {
		return this.jdbcParam;
	}

	/**
	 * {@link NeedCanUpdate}接口是否有效.
	 * 
	 * @return 如果为true，则表示{@link NeedCanUpdate}接口将无效，不会进行此接口的相关判断。默认false
	 */
	public boolean isNeedCanUpdateInterfaceInvalid() {
		return this.needCanUpdateInterfaceInvalid;
	}

	/**
	 * 取得 xmlElement创建拦截器
	 * 
	 * @return xmlElement创建拦截器
	 */
	public XmlElementInterceptor getXmlElementInterceptor() {
		return this.xmlElementInterceptor;
	}

	/**
	 * 是否在注解生成的时候，将catalog和schema参数也加进去。
	 * 
	 * @return ture表示加进去，false表示不加进去，默认true
	 */
	public boolean isCreateCatalogAndSchema() {
		return this.createCatalogAndSchema;
	}
	
	/**
	 * 是否不生成xml，如果你使用的是Provider方式处理sql，那么就不需要生成xml
	 * @return 如果生成返回false，不生成返回true
	 */
	public boolean isNoXml() {
		return this.noXml;
	}

	/**
	 * 是否不生成xml，如果你使用的是Provider方式处理sql，那么就不需要生成xml
	 * @param noXml 如果不想生成，设置true即可
	 */
	public void setNoXml(boolean noXml) {
		this.noXml = noXml;
	}
	
	/**
	 * 设置 是否生成列名称，表名称常量，默认true
	 * @param createNameConst 是否生成列名称，表名称常量
	 */
	public void setCreateNameConst(boolean createNameConst) {
		this.createNameConst = createNameConst;
	}
	/**
	 * 是否生成列名称，表名称常量
	 * @return 是否生成列名称，表名称常量
	 */
	public boolean isCreateNameConst() {
		return this.createNameConst;
	}

	/**
	 * 取得 是否仅使用常用类型，如果为true，则不会出现byte、short、float、类型，而是采用Integer和Double代替。
	 * @return the onlyCommonType
	 */
	public boolean isOnlyCommonType() {
		return this.onlyCommonType;
	}

	/**
	 * 设置 是否仅使用常用类型，如果为true，则不会出现byte、short、float、类型，而是采用Integer和Double代替。
	 * @param onlyCommonType the onlyCommonType to set
	 */
	public void setOnlyCommonType(boolean onlyCommonType) {
		this.onlyCommonType = onlyCommonType;
	}
	
	

}
